package com.xh3140.xwidget.edittext;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatImageView;
import androidx.appcompat.widget.LinearLayoutCompat;

import java.util.Objects;

public class LineEditLayout extends LinearLayoutCompat {
  private String mInputFilterRegex;
  private EditText mEditText;
  private int mEditTextPasswordType;
  private int mPasswordVisibleIconResId;
  private int mPasswordInvisibleIconResId;
  private final FrameLayout mEditViewFrame;
  private final AppCompatImageView mImageViewIcon;
  private final AppCompatImageView mImageViewClear;
  private final AppCompatImageView mImageViewPasswordToggle;

  public LineEditLayout(@NonNull Context context) {
    this(context, null);
  }

  public LineEditLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
    this(context, attrs, 0);
  }

  public LineEditLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    setOrientation(LinearLayoutCompat.HORIZONTAL);
    mEditViewFrame = new FrameLayout(context);
    mImageViewIcon = new AppCompatImageView(context);
    mImageViewClear = new AppCompatImageView(context);
    mImageViewPasswordToggle = new AppCompatImageView(context);
    final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LineEditLayout, 0, R.style.LineEditLayout);
    try {
      if (a.hasValue(R.styleable.LineEditLayout_icon_main)) {
        setIconImageResource(a.getResourceId(R.styleable.LineEditLayout_icon_main, 0));
      }
      if (a.hasValue(R.styleable.LineEditLayout_icon_clear)) {
        setClearImageResource(a.getResourceId(R.styleable.LineEditLayout_icon_clear, 0));
      }
      if (a.hasValue(R.styleable.LineEditLayout_icon_password_visible)) {
        setPasswordVisibleImageResource(a.getResourceId(R.styleable.LineEditLayout_icon_password_visible, 0));
      }
      if (a.hasValue(R.styleable.LineEditLayout_icon_password_invisible)) {
        setPasswordInvisibleImageResource(a.getResourceId(R.styleable.LineEditLayout_icon_password_invisible, 0));
      }
      if (a.hasValue(R.styleable.LineEditLayout_icon_color)) {
        setIconImageColor(a.getInt(R.styleable.LineEditLayout_icon_color, 0));
      }
      if (a.hasValue(R.styleable.LineEditLayout_regex)) {
        setInputFilterRegex(a.getString(R.styleable.LineEditLayout_regex));
      }
    } finally {
      a.recycle();
    }
    initEditViewFrame();
    initIconView();
    initIconClearView();
    initIconPasswordToggleView();
    addView(mImageViewIcon);
    addView(mEditViewFrame);
    addView(mImageViewClear);
    addView(mImageViewPasswordToggle);
  }

  public void setInputFilterRegex(@Nullable String regex) {
    mInputFilterRegex = regex;
  }

  public void setIconImageResource(@DrawableRes int resourceId) {
    mImageViewIcon.setImageResource(resourceId);
  }

  public void setClearImageResource(@DrawableRes int resourceId) {
    mImageViewClear.setImageResource(resourceId);
  }

  public void setPasswordVisibleImageResource(@DrawableRes int resourceId) {
    mPasswordVisibleIconResId = resourceId;
    if (mPasswordInvisibleIconResId == 0) {
      mPasswordInvisibleIconResId = resourceId;
    }
    updateImageViewToggleImageResource();
  }

  public void setPasswordInvisibleImageResource(@DrawableRes int resourceId) {
    if (mPasswordVisibleIconResId == 0) {
      mPasswordVisibleIconResId = resourceId;
    }
    mPasswordInvisibleIconResId = resourceId;
    updateImageViewToggleImageResource();
  }

  private void setIconImageColor(@ColorInt int color) {
    mImageViewIcon.setColorFilter(color);
    mImageViewClear.setColorFilter(color);
    mImageViewPasswordToggle.setColorFilter(color);
  }

  private int dp2px(int dp) {
    return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
  }

  private void initEditViewFrame() {
    mEditViewFrame.setLayoutParams(new LayoutParams(0, LayoutParams.MATCH_PARENT, 1f));
  }

  private void initIconView() {
    final int padding = dp2px(12);
    mImageViewIcon.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
    mImageViewIcon.setPaddingRelative(padding, padding, padding, padding);
  }

  private void initIconClearView() {
    mImageViewClear.setLayoutParams(new LayoutParams(dp2px(40), LayoutParams.MATCH_PARENT));
    mImageViewClear.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    mImageViewClear.setVisibility(View.GONE);
    mImageViewClear.setOnClickListener(v -> {
      if (mEditText != null) {
        mEditText.setText("");
        mImageViewClear.setVisibility(View.GONE);
      }
    });
  }

  private void initIconPasswordToggleView() {
    mImageViewPasswordToggle.setLayoutParams(new LayoutParams(dp2px(40), LayoutParams.MATCH_PARENT));
    mImageViewPasswordToggle.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
    mImageViewPasswordToggle.setVisibility(View.GONE);
    mImageViewPasswordToggle.setOnClickListener(v -> {
      if (mEditText == null) return;
      final int selectionStart = mEditText.getSelectionStart();
      final int selectionEnd = mEditText.getSelectionEnd();
      final int inputType = mEditText.getInputType();
      if (isPasswordInputType(inputType)) {
        mImageViewPasswordToggle.setImageResource(mPasswordInvisibleIconResId);
        mEditText.setInputType(inputType | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
      } else if (isVisiblePasswordInputType(inputType)) {
        mImageViewPasswordToggle.setImageResource(mPasswordVisibleIconResId);
        mEditText.setInputType(mEditTextPasswordType);
      } else {
        mImageViewPasswordToggle.setImageResource(0);
      }
      mEditText.setSelection(selectionStart, selectionEnd);
    });
  }

  public void updateImageViewToggleImageResource() {
    if (mEditText == null) return;
    final int inputType = mEditText.getInputType();
    if (isPasswordInputType(inputType)) {
      mImageViewPasswordToggle.setImageResource(mPasswordVisibleIconResId);
    } else if (isVisiblePasswordInputType(inputType)) {
      mImageViewPasswordToggle.setImageResource(mPasswordInvisibleIconResId);
    } else {
      mImageViewPasswordToggle.setImageResource(0);
    }
  }

  public void updateImageViewClearVisibility() {
    if (mEditText == null) return;
    if (mEditText.hasFocus()) {
      final Editable text = mEditText.getText();
      if (text != null && text.length() > 0) {
        mImageViewClear.setVisibility(View.VISIBLE);
        return;
      }
    }
    mImageViewClear.setVisibility(View.GONE);
  }

  public void updateImageViewToggleVisibility() {
    if (mEditText == null) return;
    final int inputType = mEditText.getInputType();
    if (isAnyPasswordInputType(inputType) && mEditText.hasFocus()) {
      mImageViewPasswordToggle.setVisibility(View.VISIBLE);
    } else {
      mImageViewPasswordToggle.setVisibility(View.GONE);
    }
  }

  public void setEditText(@NonNull EditText editText) {
    if (mEditText != null) {
      throw new IllegalArgumentException("We already have an EditText, can only have one");
    }
    mEditText = Objects.requireNonNull(editText);
    mEditText.setSingleLine(true);
    if (mInputFilterRegex != null) {
      mEditText.setFilters(new InputFilter[]{new RegexInputFilter()});
    }
    final int inputType = mEditText.getInputType();
    if (isPasswordInputType(inputType)) {
      mEditTextPasswordType = inputType;
    } else if (isVisiblePasswordInputType(inputType)) {
      mEditTextPasswordType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
    } else {
      mEditTextPasswordType = InputType.TYPE_NULL;
    }
    // must refresh input type.
    mEditText.setInputType(inputType);
    // fix password mode font.
    if (isAnyPasswordInputType(inputType)) {
      mEditText.setTypeface(Typeface.DEFAULT);
    }
    mEditText.addTextChangedListener(new TextWatcher() {
      @Override
      public void afterTextChanged(Editable s) {
        updateImageViewClearVisibility();
      }

      @Override
      public void beforeTextChanged(CharSequence s, int start, int count, int after) {
      }

      @Override
      public void onTextChanged(CharSequence s, int start, int before, int count) {
      }
    });
    mEditText.setOnFocusChangeListener((v, hasFocus) -> {
      updateImageViewClearVisibility();
      updateImageViewToggleVisibility();
    });
    updateImageViewToggleImageResource();
  }

  @Override
  public void addView(View child, int index, ViewGroup.LayoutParams params) {
    if (child instanceof EditText) {
      final FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams(params);
      flp.gravity = Gravity.CENTER_VERTICAL | (flp.gravity & ~Gravity.VERTICAL_GRAVITY_MASK);
      mEditViewFrame.addView(child, flp);
      setEditText((EditText) child);
    } else {
      super.addView(child, index, params);
    }
  }

  public class RegexInputFilter implements InputFilter {
    @Override
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
      if (mInputFilterRegex == null || mInputFilterRegex.isEmpty()) {
        return null;
      }
      if (source == null || source.length() == 0) {
        return "";
      }
      final String target = String.valueOf(dest.subSequence(0, dstart)) + source + dest.subSequence(dend, dest.length());
      if (target.matches(mInputFilterRegex)) {
        return null;
      }
      return "";
    }
  }

  public static boolean isAnyPasswordInputType(int inputType) {
    return isPasswordInputType(inputType) || isVisiblePasswordInputType(inputType);
  }

  public static boolean isPasswordInputType(int inputType) {
    final int variation = inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
    return variation == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD)
            || variation == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_PASSWORD)
            || variation == (InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
  }

  public static boolean isVisiblePasswordInputType(int inputType) {
    final int variation = inputType & (InputType.TYPE_MASK_CLASS | InputType.TYPE_MASK_VARIATION);
    return variation == (InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
  }
}
